package com.ybear.ybutils.utils;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import androidx.annotation.AnimRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.Size;
import androidx.core.util.Consumer;
import androidx.core.view.ViewCompat;

import com.ybear.ybutils.utils.handler.Handler;
import com.ybear.ybutils.utils.handler.HandlerManage;
import com.ybear.ybutils.utils.log.LogUtil;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 动画池
 */
public class AnimationPool {
    private static final String TAG = "AnimationPool";
    private static final int[] mCenterAlign = new int[ 2 ];
    private static final int[] mTmpXY = new int[ 2 ];
    private static final int[] mTmpViewSize = new int[ 2 ];
    private static final Queue<AnimationHolder> mAnimationBuilderQueue = new ConcurrentLinkedQueue<>();
    private static final List<AnimationHolder> mRunningQueue = new ArrayList<>();
    private static final Map<Integer, Recycle> mRecycleMap = new HashMap<>();
    private static final Interpolator defaultInterpolator = new LinearInterpolator();

    private AnimationPool() {}
    public static AnimationPool get() { return H.I; }
    private static final class H { private static final AnimationPool I = new AnimationPool(); }

    public CreateQueue with(@NonNull Window window) {
        return new CreateQueue( new AnimationBuilder( this, window ) );
    }

    public void release(Window window) {
        Handler handler = HandlerManage.create();
        //发起异步处理
        handler.postAsync(() -> {
            ViewGroup root = getContentView( window );
            if( root != null ) {
                for( Integer id : mRecycleMap.keySet() ) {
                    try {
                        View view = root.findViewById( id );
                        if( view == null ) continue;
                        //同步返回移除View
                        handler.post( () -> {
                            try { root.removeView( view ); } catch (Exception ignored) {}
                        } );
                        LogUtil.d( TAG, "release -> remove -> success, id:%s", id );
                    }catch(Exception e) {
                        e.printStackTrace();
                        LogUtil.d( TAG, "release -> remove -> error, id:%s", id );
                    }
                }
            }
            try {
                for( AnimationHolder h : mAnimationBuilderQueue ) {
                    if( h == null ) continue;
                    h.release();
                    LogUtil.d( TAG, "release -> clear -> " +
                            "mAnimationBuilderQueue success, id:%s", h.getId()
                    );
                }
                mAnimationBuilderQueue.clear();
            }catch(Exception e) {
                e.printStackTrace();
                LogUtil.d( TAG, "release -> clear -> mAnimationBuilderQueue error" );
            }
            try {
                for( AnimationHolder h : mRunningQueue ) {
                    if( h == null ) continue;
                    h.release();
                    LogUtil.d( TAG, "release -> clear -> mRunningQueue success, id:%s", h.getId()
                    );
                }
                mRunningQueue.clear();
            }catch(Exception e) {
                e.printStackTrace();
                LogUtil.d( TAG, "release -> clear -> mRunningQueue error" );
            }
        });
    }

    @Nullable
    private ViewGroup getContentView(Window window) {
        return window == null ? null : window.findViewById( android.R.id.content );
    }

    private static void getViewSize(@NonNull View view, int[] size) {
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        int w, h;
        if( lp != null && lp.width > 0 && lp.height > 0 ) {
            w = lp.width;
            h = lp.height;
        } else if( view.getWidth() > 0 && view.getHeight() > 0 ) {
            w = view.getWidth();
            h = view.getHeight();
        }else {
            //测量
            view.measure( 0, 0 );
            w = Math.max( view.getMeasuredWidth(), view.getWidth() );
            h = Math.max( view.getMeasuredHeight(), view.getHeight() );
        }
        size[ 0 ] = w;
        size[ 1 ] = h;
    }

    public void getCenterAlign(ViewGroup.LayoutParams lp, @Size( 2 ) int[] centerAlign, boolean isAnimView) {
        if( lp == null || centerAlign == null || centerAlign.length < 2 ) return;
        centerAlign[ 0 ] = getCenterAlign( lp.width, isAnimView );
        centerAlign[ 1 ] = getCenterAlign( lp.height, isAnimView );
        LogUtil.d(
                TAG, "AnimationPool -> getCenterAlign -> width:%s, height:%s, caX:%s, caY:%s",
                lp.width, lp.height, centerAlign[ 0 ], centerAlign[ 1 ]
        );
    }

    @Size( 2 )
    public int[] getCenterAlign(ViewGroup.LayoutParams lp, boolean isAnimView) {
        getCenterAlign( lp, mCenterAlign, isAnimView );
        return mCenterAlign;
    }

    public int getCenterAlignOfWidth(ViewGroup.LayoutParams lp, boolean isAnimView) {
        return getCenterAlign( lp, isAnimView )[ 0 ];
    }
    public int getCenterAlignOfWidth(ViewGroup.LayoutParams lp) {
        return getCenterAlignOfWidth( lp, false );
    }

    public int getCenterAlignOfHeight(ViewGroup.LayoutParams lp, boolean isAnimView) {
        return getCenterAlign( lp, isAnimView )[ 1 ];
    }
    public int getCenterAlignOfHeight(ViewGroup.LayoutParams lp) {
        return getCenterAlignOfHeight( lp, false );
    }

    public int getCenterAlign(int size, boolean isAnimView) {
        int ca = 0;
        try {
            ca = size > 0 ?
                    ObjUtils.parseInt( ObjUtils.parseDouble( size ) / ( isAnimView ? 2D : 4D ) ) :
                    0;
        }catch(Exception e) {
            e.printStackTrace();
        }
        LogUtil.d(
                TAG, "AnimationPool -> getCenterAlign -> size:%s, centerAlign:%s",
                size, ca
        );
        return ca;
    }

    private static class Recycle {
        private int id;
        private View view;
        private int holderType;
        private boolean isRecycle;
        private boolean isNewRecycle;
        public int getId() { return id; }
        public View getView() { return view; }
        public void setView(View view) {
            this.view = view;
            id = view == null ? 0 : view.getId();
        }
        public int getHolderType() { return holderType; }
        public void setHolderType(int holderType) { this.holderType = holderType; }
        public boolean isRecycle() { return isRecycle; }
        public void setRecycle(boolean recycle) { isRecycle = recycle; }
        public boolean isNewRecycle() { return isNewRecycle; }
        public void setNewRecycle(boolean newRecycle) { isNewRecycle = newRecycle; }
    }

    public interface OnAnimationBuilder {
        @Retention( RetentionPolicy.SOURCE )
        @IntDef({ AlignType.DEFAULT, AlignType.HORIZONTAL, AlignType.VERTICAL, AlignType.IN_PARENT })
        @interface AlignType {
            int DEFAULT = 0;        //不对齐
            int HORIZONTAL = 1;     //水平对齐
            int VERTICAL = 2;       //垂直对齐
            int IN_PARENT = 3;      //中心对齐
        }

        /**
         创建一个移动的View
         @param context     上下文
         @param holder      ViewHolder
         @param holderType      ViewHolder类型（可以根据类型设置不同的Holder）
         @return            创建好的View
         */
        @NonNull
        View onCreateAnimView(@NonNull Context context, @NonNull AnimationHolder holder, int holderType);
        /**
         View的布局参数
         @param holder      ViewHolder
         @param lp          布局参数
         @param holderType      ViewHolder类型（可以根据类型设置不同的Holder）
         */
        void onAnimViewLayoutParams(@NonNull AnimationHolder holder, @NonNull ViewGroup.LayoutParams lp, int holderType);

        /**
         绑定ViewHolder
         @param holder      ViewHolder
         @param animView    移动的View
         @param holderType  ViewHolder类型（可以根据类型设置不同的Holder）
         @param call        当return返回 null时，会被调用（场景：需要图片加载完毕才绑定Holder的情况）
         @return            返回null时不生效，需要调用 {@param call}
         */
        @Nullable
        AnimationHolder onBindHolder(@NonNull AnimationHolder holder, @NonNull View animView,
                                     @Nullable Object data, int holderType,
                                     @NonNull Consumer<AnimationHolder> call);
        /**
         启用起始View的控件对齐（启用后，原先移动View以左上角（默认）坐标对齐，改为起始View的中心原点对齐）
         @param holderType      ViewHolder类型（可以根据类型设置不同的Holder）
         @return                对齐类型 {@link AlignType}
         */
        @AlignType
        int onEnableFromAlign(int holderType);
        /**
         启用目标View的控件对齐（启用后，原先移动View以左上角（默认）坐标对齐，改为目标View的中心原点对齐）
         @param holderType      ViewHolder类型（可以根据类型设置不同的Holder）
         @return                对齐类型 {@link AlignType}
         */
        @AlignType
        int onEnableToAlign(int holderType);
    }

    public static class AnimationXY {
        private ValueAnimator mValueAnimatorX;
        private ValueAnimator mValueAnimatorY;

        private int[] valuesX;
        private int[] valuesY;
        private long durationX = 0;
        private long durationY = 0;
        private Interpolator interpolatorX;
        private Interpolator interpolatorY;

        @NonNull
        @Override
        public String toString() {
            return "AnimationXY{" +
                    "mValueAnimatorX=" + mValueAnimatorX +
                    ", mValueAnimatorY=" + mValueAnimatorY +
                    ", valuesX=" + Arrays.toString( valuesX ) +
                    ", valuesY=" + Arrays.toString( valuesY ) +
                    ", durationX=" + durationX +
                    ", durationY=" + durationY +
                    ", interpolatorX=" + interpolatorX +
                    ", interpolatorY=" + interpolatorY +
                    '}';
        }

        public static AnimationXY create() { return new AnimationXY(); }

        public AnimationXY updateX(int... arr) {
            this.valuesX = arr;
            return this;
        }

        public AnimationXY updateY(int... arr) {
            this.valuesY = arr;
            return this;
        }

        public AnimationXY updateX(int fx, int tx, int[] paths) {
            this.valuesX = ObjUtils.toNewIntArrays( fx, tx, paths );
            return this;
        }

        public AnimationXY updateY(int fy, int ty, int[] paths) {
            this.valuesY = ObjUtils.toNewIntArrays( fy, ty, paths );
            return this;
        }

        public AnimationXY update(int fromX, int fromY, int toX, int toY) {
            return update( fromX, fromY, toX, toY, null, null );
        }

        public AnimationXY update(int fromX, int fromY, int toX, int toY,
                                  int[] pathsX, int[] pathsY) {
            valuesX = ObjUtils.toNewIntArrays( fromX, toX, pathsX );
            valuesY = ObjUtils.toNewIntArrays( fromY, toY, pathsY );
            return this;
        }

        public void setDuration(long duration) {
            setDurationX( duration );
            setDurationY( duration );
        }
        public void setDurationX(long duration) { durationX = duration; }
        public void setDurationY(long duration) { durationY = duration; }

        public void setInterpolator(Interpolator interpolator) { setInterpolatorX( interpolator ); }
        public void setInterpolatorX(Interpolator interpolator) { interpolatorX = interpolator; }
        public void setInterpolatorY(Interpolator interpolator) { interpolatorY = interpolator; }

//        private long calcTranslateDuration(int distanceX, int distanceY) {
//            /* 假设
//             *    fromX:0, toX:120, duration:800, distance:120 ( abs( 0 - 120 ) )
//             *    fromY:0, toY:110, duration:?  , distance:110 ( abs( 0 - 110 ) )
//             *
//             *    maxDistance:120
//             *    minDistance:110
//             *    calcDuration:800 (durationY 最小Distance的另一个duration)
//             *    rate:7.27 ( 800(max duration) / 110(min distance) )
//             *    newDuration:872.4 ≈ 872 ( max distance / rate )
//             *
//             *    durationX:800, durationY:873
//             * */
//            int maxDistance = Math.max( distanceX, distanceY );
//            int minDistance = Math.min( distanceX, distanceY );
//            long calcDuration = minDistance == distanceX ? durationY : durationX;
//
//            //每次移动的频率（最大速度(Duration) / 距离(如果速度是x，则取y的速度)）
//            double rate = ObjUtils.parseDouble( calcDuration ) / ObjUtils.parseDouble( maxDistance );
//            long newDuration = ObjUtils.parseLong(
//                    ObjUtils.parseDouble( minDistance ) * ObjUtils.parseDouble( rate )
//            );
//            LogUtil.d( TAG, "startValues -> Translate -> " +
//                            "duration:%s|%s, distance:%s|%s, rate:%s, minDistance:%s, calcDuration:%s, newDuration:%s",
//                    durationX, durationY, distanceX, distanceY, rate, minDistance, calcDuration, newDuration
//            );
//            return newDuration;
//        }

        public void startValues(View view, Animator.AnimatorListener listener, boolean isTranslate) {
            if( view == null ) return;
//            int distanceX = -1;
//            int distanceY = -1;
            if( valuesX != null && valuesX.length > 1 ) {
//                distanceX = Math.abs( valuesX[ 0 ] - valuesX[ valuesX.length - 1 ] );
                mValueAnimatorX = ValueAnimator.ofInt( valuesX );
            }
            if( valuesY != null && valuesY.length > 1 ) {
//                distanceY = Math.abs( valuesY[ 0 ] - valuesY[ valuesY.length - 1 ] );
                mValueAnimatorY = ValueAnimator.ofInt( valuesY );
            }
            LogUtil.d( TAG, "startValues -> from:%s|%s, xArr:%s, yArr:%s",
                    view.getX(), view.getY(), Arrays.toString( valuesX ), Arrays.toString( valuesY )
            );
//            //平移模式下取最大Duration算出另一个的Duration，保证平移一致性
//            if( isTranslate && distanceX != -1 && distanceY != -1 ) {
//                long newDuration = 0;
//                try {
//                    newDuration = calcTranslateDuration( distanceX, distanceY );
//                }catch(Exception e) {
//                    e.printStackTrace();
//                }
//                if( durationX > durationY ) {
//                    durationY = newDuration;
//                }else {
//                    durationX = newDuration;
//                }
//            }
            view.setVisibility( View.VISIBLE );
            if( mValueAnimatorX != null ) {
                if( interpolatorX != null ) mValueAnimatorX.setInterpolator( interpolatorX );
                mValueAnimatorX.setDuration( durationX );
                mValueAnimatorX.addUpdateListener(
                        anim -> view.setX( ObjUtils.parseInt( anim.getAnimatedValue() ) )
                );
                if( listener != null ) mValueAnimatorX.addListener( listener );
                mValueAnimatorX.start();
            }
            if( mValueAnimatorY != null ) {
                if( interpolatorY != null ) mValueAnimatorY.setInterpolator( interpolatorY );
                mValueAnimatorY.setDuration( durationY );
                mValueAnimatorY.addUpdateListener(
                        anim -> view.setY( ObjUtils.parseInt( anim.getAnimatedValue() ) )
                );
                if( listener != null ) {
                    mValueAnimatorY.addListener( listener );
                }
                mValueAnimatorY.start();
            }
        }

        public void startValues(View view) { startValues( view, null, false ); }

        public void start() {
            if( mValueAnimatorX != null ) mValueAnimatorX.start();
            if( mValueAnimatorY != null ) mValueAnimatorY.start();
        }

        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        public void pause() {
            if( mValueAnimatorX != null ) mValueAnimatorX.pause();
            if( mValueAnimatorY != null ) mValueAnimatorY.pause();
        }

        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        public void resume() {
            if( mValueAnimatorX != null ) mValueAnimatorX.resume();
            if( mValueAnimatorY != null ) mValueAnimatorY.resume();
        }

        public void cancel() {
            if( mValueAnimatorX != null ) mValueAnimatorX.cancel();
            if( mValueAnimatorY != null ) mValueAnimatorY.cancel();
        }

        public void stop() {
            if( mValueAnimatorX != null ) mValueAnimatorX.end();
            if( mValueAnimatorY != null ) mValueAnimatorY.end();
        }
    }

    /**
     动画Holder
     */
    public static class AnimationHolder {
        /**
         这两个参数应用在设置x、y时单独使用
         例如:
         setToView( 100, START_OUTSIDE ) y的目标位置在  ->  -( 0 + View的宽度 )
         setToView( 100, END_OUTSIDE ) y的目标位置在    ->  屏幕高度 + View的宽度

         setToView( START_OUTSIDE, 100 ) y的目标位置在  ->  -( 0 + View的高度 )
         setToView( END_OUTSIDE, 100 ) y的目标位置在    ->  屏幕高度 + View的高度
         @see AnimationHolder#START_OUTSIDE
         @see AnimationHolder#END_OUTSIDE
         */
        //Top 和 Left 的位置
        public static final int START_OUTSIDE = Integer.MAX_VALUE;
        //Bottom 和 Right 的位置
        public static final int END_OUTSIDE = Integer.MIN_VALUE;
        private final long id;
        private final Object data;
        private final int holderType;
        private final OnAnimationBuilder onAnimationBuilder;
        private final AtomicInteger atoIsStart;

        private View fromView;
        private View toView;
        private Interpolator xInterpolator;
        private Interpolator yInterpolator;
        private long xDuration;
        private long yDuration;
        private int fromViewX;
        private int fromViewY;
        private int[] pathXs;
        private int[] pathYs;
        private int toViewX;
        private int toViewY;
        private long fromDelayMillis;
        private long toDelayMillis;
        private long fromScaleMillis;
        private long toScaleMillis;
        private boolean isHideFromView;
        private int hideFromViewVisibility = View.INVISIBLE;
        private float animViewAlpha = 1.0F;
        private boolean enableTranslate;

        private Animation fromAddAnimation;
        private Animation toRemoveAnimation;

        private AnimationXY animatorXY;

        public AnimationHolder(long id, Object data, int holderType, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this.id = id < 0 ? ObjUtils.parseInt( Math.random() * Integer.MAX_VALUE ) : id;
            this.data = data;
            this.holderType = holderType;
            this.onAnimationBuilder = onAnimationBuilder;
            atoIsStart = new AtomicInteger();
        }

        public AnimationHolder(Object data, int holderType, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this( -1, data, holderType, onAnimationBuilder );
        }

        public AnimationHolder(int holderType, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this( null, holderType, onAnimationBuilder );
        }

        public AnimationHolder(@NonNull OnAnimationBuilder onAnimationBuilder) {
            this( 0, onAnimationBuilder );
        }

        public AnimationHolder(long id, Object data, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this( id, data, 0, onAnimationBuilder );
        }

        public AnimationHolder(long id, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this( id, null, onAnimationBuilder );
        }

        public AnimationHolder(Object data, @NonNull OnAnimationBuilder onAnimationBuilder) {
            this( -1, data, onAnimationBuilder );
        }

        void release() {
            try {
                fromView = null;
                toView = null;
                xInterpolator = null;
                yInterpolator = null;
                fromAddAnimation = null;
                toRemoveAnimation = null;
                animatorXY = null;
                hideFromViewVisibility = View.INVISIBLE;
                xDuration = yDuration = 0;
                fromViewX = fromViewY = 0;
                pathXs = null;
                pathYs = null;
                toViewX = toViewY = 0;
                animViewAlpha = 1.0F;
                fromDelayMillis = 0L;
                toDelayMillis = 0L;
                fromScaleMillis = 0L;
                toScaleMillis = 0L;
                isHideFromView = false;
                enableTranslate = false;
                System.gc();
            }catch(Exception e) {
                e.printStackTrace();
            }
        }

        @NonNull
        @Override
        public String toString() {
            return "AnimationHolder{" +
                    "id=" + id +
                    ", data=" + data +
                    ", holderType=" + holderType +
                    ", onAnimationBuilder=" + onAnimationBuilder +
                    ", atoIsStart=" + atoIsStart +
                    ", fromView=" + fromView +
                    ", toView=" + toView +
                    ", xInterpolator=" + xInterpolator +
                    ", yInterpolator=" + yInterpolator +
                    ", xDuration=" + xDuration +
                    ", yDuration=" + yDuration +
                    ", fromViewX=" + fromViewX +
                    ", fromViewY=" + fromViewY +
                    ", pathXs=" + Arrays.toString( pathXs ) +
                    ", pathYs=" + Arrays.toString( pathYs ) +
                    ", toViewX=" + toViewX +
                    ", toViewY=" + toViewY +
                    ", fromDelayMillis=" + fromDelayMillis +
                    ", toDelayMillis=" + toDelayMillis +
                    ", fromScaleMillis=" + fromScaleMillis +
                    ", toScaleMillis=" + toScaleMillis +
                    ", isHideFromView=" + isHideFromView +
                    ", hideFromViewVisibility=" + hideFromViewVisibility +
                    ", animViewAlpha=" + animViewAlpha +
                    ", enableTranslate=" + enableTranslate +
                    ", fromAddAnimation=" + fromAddAnimation +
                    ", toRemoveAnimation=" + toRemoveAnimation +
                    ", animatorXY=" + animatorXY +
                    '}';
        }

        public long getId() { return id; }

        public void setFromView(View view) { fromView = view; }
        public void setToView(View view) { toView = view; }

        public void setFromView(int x, int y) { setFromXY( x, y ); }
        public void setToView(int x, int y) { setToXY( x, y ); }

        public void setPath(int[] xs, int[] ys) {
            pathXs = xs;
            pathYs = ys;
        }


        public void setFromDelayMillis(long delayMillis) { fromDelayMillis = delayMillis; }
        public void setToDelayMillis(long delayMillis) { toDelayMillis = delayMillis; }


        public void setFromScaleMillis(long fromScaleMillis) { this.fromScaleMillis = fromScaleMillis; }
        public void setToScaleMillis(long toScaleMillis) { this.toScaleMillis = toScaleMillis; }

        public void setXInterpolator(Interpolator interpolator) { xInterpolator = interpolator; }
        public void setYInterpolator(Interpolator interpolator) { yInterpolator = interpolator; }

        public void setXDuration(long xDuration) { this.xDuration = xDuration; }
        public void setYDuration(long yDuration) { this.yDuration = yDuration; }
        public void setDuration(long duration, boolean enable) {
            setXDuration( duration );
            setYDuration( duration );
            enableTranslate = enable;
        }
        public boolean isEnableTranslate() { return enableTranslate; }

        void setFromXY(int x, int y) {
            fromViewX = x;
            fromViewY = y;
        }
        void setToXY(int x, int y) {
            toViewX = x;
            toViewY = y;
        }

        public void setFromAddAnimation(Animation animation) { fromAddAnimation = animation; }
        public void setFromAddAnimation(Context context, @AnimRes int resId) {
            setFromAddAnimation( AnimationUtils.loadAnimation( context, resId ) );
        }
        public void setFromAddAlpha(long dur) {
            AlphaAnimation animation = new AlphaAnimation( 0, 1 );
            animation.setDuration( dur );
            setFromAddAnimation( animation );
        }

        public void setToRemoveAnimation(Animation animation) { toRemoveAnimation = animation; }
        public void setToRemoveAnimation(Context context, @AnimRes int resId) {
            setToRemoveAnimation( AnimationUtils.loadAnimation( context, resId ) );
        }
        public void setToRemoveAlpha(long dur) {
            AlphaAnimation animation = new AlphaAnimation( 1, 0 );
            animation.setDuration( dur );
            setToRemoveAnimation( animation );
        }

        public void clearFromAddAnimation() { setFromAddAnimation( null ); }
        public void clearToRemoveAnimation() { setToRemoveAnimation( null ); }

        @Nullable
        public View getFromView() { return fromView; }
        @Nullable
        public View getToView() { return toView; }

        public int getFromViewX() { return fromViewX; }
        public int getFromViewY() { return fromViewY; }
        public void getFromViewXY(@NonNull @Size( 2 ) int[] xy) {
            xy[ 0 ] = getFromViewX();
            xy[ 1 ] = getFromViewY();
        }
        public int getToViewX() { return toViewX; }
        public int getToViewY() { return toViewY; }
        public void getToViewXY(@NonNull @Size( 2 ) int[] xy) {
            xy[ 0 ] = getToViewX();
            xy[ 1 ] = getToViewY();
        }

        public int[] getPathXs() { return pathXs; }
        public int[] getPathYs() { return pathYs; }

        public void setHideFromView(boolean hide) { isHideFromView = hide; }
        public void setHideFromViewVisibility(int visibility) { hideFromViewVisibility = visibility; }

        public long getXDuration() { return xDuration; }
        public long getYDuration() { return yDuration; }

        Interpolator getXInterpolator() { return xInterpolator; }
        Interpolator getYInterpolator() { return yInterpolator; }

        boolean isAnimStart() {
            int count = 0;
            if( xDuration > 0 ) count++;
            if( yDuration > 0 ) count++;
            return atoIsStart.incrementAndGet() >= count;
        }
        boolean isAnimEnd() { return atoIsStart.decrementAndGet() <= 0; }
        OnAnimationBuilder getOnAnimationBuilder() { return onAnimationBuilder; }
        Object getData() { return data; }
        int getHolderType() { return holderType; }

        AnimationXY getAnimatorXY() { return animatorXY; }
        void setAnimatorXY(AnimationXY xy) { animatorXY = xy; }

        Animation getFromAddAnimation() { return fromAddAnimation; }
        Animation getToRemoveAnimation() { return toRemoveAnimation; }

        long getFromDelayMillis() { return fromDelayMillis; }
        long getToDelayMillis() { return toDelayMillis; }

        long getFromScaleMillis() { return fromScaleMillis; }
        long getToScaleMillis() { return toScaleMillis; }

        public boolean isHideFromView() { return isHideFromView; }
        public int getHideFromViewVisibility() { return hideFromViewVisibility; }

        public float getAnimViewAlpha() { return animViewAlpha; }
        public void setAnimViewAlpha(float alpha) { animViewAlpha = alpha; }
    }

    public abstract static class AnimationShadowHolder {
        private final List<AnimationPool.AnimationHolder> holders = new ArrayList<>();
        public AnimationShadowHolder(int shadowCount, boolean enableAlpha) {
            create( shadowCount, enableAlpha );
        }

        public AnimationShadowHolder(int shadowCount) {
            this( shadowCount, false );
        }

        @NonNull
        public abstract AnimationPool.AnimationHolder onCreateHolder(int index);
        public abstract int onShadowIntervalDuration(int index);

        private void create(int shadowCount, boolean enableAlpha) {
            AnimationPool.AnimationHolder holder = onCreateHolder( 0 );
            long initFromDelay = holder.getFromDelayMillis();
            long initToDelay = holder.getToDelayMillis();
            int shadowIntervalDur = 0;
            float alpha = 1.0F;
            holders.add( holder );
            for( int i = 1; i <= shadowCount; i++ ) {
                holder = onCreateHolder( i );
                shadowIntervalDur += onShadowIntervalDuration( i );
                holder.setFromDelayMillis( shadowIntervalDur + initFromDelay );
                holder.setToDelayMillis( initToDelay );
                //启用alpha渐变
                if( enableAlpha ) holder.setAnimViewAlpha( alpha / ( i + 1 ) );

                holders.add( holder );
            }
        }

        List<AnimationPool.AnimationHolder> build() { return holders; }
    }

    public static class CreateQueue {
        private final AnimationBuilder mAnimationBuilder;
        public CreateQueue(AnimationBuilder builder) {
            mAnimationBuilder = builder;
        }

        public CreateQueue addAnimationHolderAll(List<AnimationHolder> builders) {
            mAnimationBuilder.addAll( builders );
            return this;
        }

        public CreateQueue addAnimationHolderAll(AnimationHolder... builders) {
            mAnimationBuilder.addAll( builders );
            return this;
        }

        public CreateQueue addAnimationHolder(AnimationHolder builders) {
            mAnimationBuilder.add( builders );
            return this;
        }

        public CreateQueue addAnimationHolder(@NonNull AnimationShadowHolder builder) {
            return addAnimationHolderAll( builder.build() );
        }

        public AnimationBuilder createQueue() { return mAnimationBuilder; }

        public int getCount() { return mAnimationBuilder.getCount(); }
    }

    public static class AnimationBuilder {
        @NonNull
        private final AnimationPool mPool;
        @NonNull
        private final Window mWindow;
        private int state = 0;  //0: 默认状态, 1: 处理队列中, 2: 暂停队列, 3: 取消队列

        public AnimationBuilder(@NonNull AnimationPool pool, @NonNull Window window) {
            mPool = pool;
            mWindow = window;
        }

        Context getContext() { return mWindow.getContext(); }

        AnimationBuilder add(AnimationHolder holder) {
            mAnimationBuilderQueue.add( holder );
            return this;
        }

        AnimationBuilder addAll(AnimationHolder... holders) {
            return addAll( Arrays.asList( holders ) );
        }

        AnimationBuilder addAll(List<AnimationHolder> holders) {
            mAnimationBuilderQueue.addAll( holders );
            return this;
        }

        int getCount() { return mAnimationBuilderQueue.size(); }

        /**
         * 开始动画队列
         */
        public void startQueuePost() { post( this::queue ); }
        /**
         * 开始动画队列
         */
        public void startQueue() { queue(); }

        /**
         * 开始动画队列 - 同时播放
         */
        public void startQueueSyncPost() { post( this::sync ); }
        /**
         * 开始动画队列 - 同时播放
         */
        public void startQueueSync() { sync(); }

        /**
         * 暂停动画队列
         */
        @RequiresApi( Build.VERSION_CODES.KITKAT )
        public void pauseQueue() {
            state = 2;
            doValueAnimator( 2 );
            LogUtil.d( TAG, "PauseQueue..." );
        }

        /**
         * 取消动画队列
         */
        public void cancelQueue() {
            state = 3;
            doValueAnimator( 3 );
            LogUtil.d( TAG, "CancelQueue..." );
        }

        /**
         * 停止动画队列
         */
        public void stopQueue() {
            state = 0;
            doValueAnimator( 4 );
            mRunningQueue.clear();
            LogUtil.d( TAG, "StopQueue..." );
        }

        public boolean isPause() { return state == 2; }
        public boolean isCancel() { return state == 3; }
        public boolean isStop() { return state == 4; }

        private boolean isWaiting() { return isPause() || isCancel(); }

        private void queue() {
            if( isWaiting() ) {
                doValueAnimator( 0 );
                return;
            }
            //队列模式下运行时，不用重新调用
            if( state == 1 ) return;
            state = 1;
            poll( "queue", result -> {
                if( !result ) return;
                state = 0;
                queue();
            });
        }
        private void sync() {
            if( isWaiting() ) {
                doValueAnimator( 1 );
                return;
            }
            state = 1;
            AnimationHolder holder = poll( "sync", null );
            if( holder != null ) sync();
        }

        private boolean post(@NonNull Runnable r, long delayMillis) {
            View decorView = mWindow.getDecorView();
            if( decorView == null ) {
                r.run();
                return true;
            }
            return delayMillis > 0 ? decorView.postDelayed( r, delayMillis ) : decorView.post( r );
        }
        private boolean post(@NonNull Runnable r) { return post( r, 0 ); }

        /**
         处理动画
         @param type    0: Queue, 1: Sync, 2: Pause, 3: Cancel, 4: Stop
         */
        private void doValueAnimator(int type) {
            LogUtil.d( TAG, "doValueAnimator -> type:%s, runningSize:%s", type, mRunningQueue.size() );
            //status -> 0: queue, 1: sync
            if( mRunningQueue.size() == 0 ) return;
            //队列。只处理第一个Holder
            if( type == 0 ) {
                try {
                    doValueAnimator( mRunningQueue.get( 0 ), type );
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return;
            }
            //异步。处理全部Holder
            for( AnimationHolder holder : mRunningQueue ) {
                doValueAnimator( holder, type );
            }
        }

        private void doValueAnimator(AnimationHolder holder, int status) {
            if( holder == null ) {
                LogUtil.e( TAG, "doValueAnimator -> holder is null. status:%s", status );
                return;
            }
            doValueAnimator( holder.getAnimatorXY(), status );
//            doValueAnimator( holder.getValueAnimatorY(), status );
        }

        private void doValueAnimator(AnimationXY va, int status) {
            if( va == null ) {
                LogUtil.e( TAG, "doValueAnimator -> AnimationXY is null. status:%s", status );
                return;
            }
            switch ( status ) {
                case 0: case 1:      //播放动画。0: queue, 1: sync
                    if ( isWaiting() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ) {
                        va.resume();
                    }else {
                        va.start();
                    }
                    break;
                case 2:             //暂停动画
                    if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ) {
                        va.pause();
                    }else {
                        va.cancel();
                    }
                    break;
                case 3:             //取消动画
                    va.cancel();
                    break;
                case 4:             //停止动画
                    va.stop();
                    break;
            }
            LogUtil.d( TAG, "doValueAnimator -> status:%s, va:%s", status, va );
        }

        private AnimationHolder poll(String tag, Consumer<Boolean> call) {
            AnimationHolder holder = mAnimationBuilderQueue.poll();
            if( holder == null ) {
                state = 0;
                LogUtil.d( TAG, "%s finish", tag );
                mRecycleMap.clear();
                return null;
            }
            mRunningQueue.add( holder );
            if( holder.isHideFromView() ) {
                setVisibility( holder.getFromView(), holder.getHideFromViewVisibility() );
            }
            long id = holder.getId();
            startAnim( holder, result -> {
                if( result ) {
                    holder.release();
                    mRunningQueue.remove( holder );
                    LogUtil.d( TAG, "poll -> startAnim -> %s id:%s", tag, id );
                }
                if( call != null ) call.accept( result );
            } );
            LogUtil.d( TAG, "poll -> %s id:%s, running...", tag, id );
            return holder;
        }

        void startAnim(AnimationHolder holder, Consumer<Boolean> call) {
            if( holder == null ) {
                LogUtil.d( TAG, "startAnim -> animBean is null" );
                return;
            }
            ViewGroup vgContent = mPool.getContentView( mWindow );
            if( vgContent == null ) {
                LogUtil.d( TAG, "startAnim -> content is null" );
                return;
            }
            OnAnimationBuilder onAnimationBuilder = holder.getOnAnimationBuilder();
            if( onAnimationBuilder == null ) {
                LogUtil.d( TAG, "startAnim -> animationBuilder is null" );
                return;
            }
            //获取一个复用Recycle
            Recycle recycle = obtainRecycle( holder );

            /* 初始化坐标 */
            View fromView = holder.getFromView();
            View toView = holder.getToView();
            View animView = recycle.getView();
            ViewGroup.LayoutParams lpAnimView = animView.getLayoutParams();

            /* 获取起始和目标View在Window窗口中的位置 */
            if( fromView != null ) {
                fromView.getLocationInWindow( mTmpXY );
                holder.setFromXY( mTmpXY[ 0 ], mTmpXY[ 1 ] );
            }
            if( toView != null ) {
                toView.getLocationInWindow( mTmpXY );
                holder.setToXY( mTmpXY[ 0 ], mTmpXY[ 1 ] );
            }
            //获取animView的高度和宽度
            getViewSize( animView, mTmpViewSize );
            lpAnimView.width = mTmpViewSize[ 0 ];
            lpAnimView.height = mTmpViewSize[ 1 ];
            animView.setLayoutParams( lpAnimView );

            //绑定回调
            Consumer<AnimationHolder> callBindHolder = bindHolder ->
                    doBindHolder( bindHolder, vgContent, recycle, call );
            //绑定Holder
            AnimationHolder bindHolder = onAnimationBuilder.onBindHolder(
                    holder, recycle.getView(), holder.getData(), holder.getHolderType(), callBindHolder
            );
            //绑定的Holder如果返回null，表示调用者需要手动触发callBindHolder回调，否则不执行。
            if( bindHolder != null ) callBindHolder.accept( bindHolder );
        }

        /**
         获取一个复用Recycle
         @param holder  Holder
         @return        Recycle
         */
        private Recycle obtainRecycle(AnimationHolder holder) {
            OnAnimationBuilder onAnimationBuilder = holder.getOnAnimationBuilder();
            int type = holder.getHolderType();
            View animView = null;
            Recycle recycle = null;
            //获取一个复用Recycle
            for( Integer key : mRecycleMap.keySet() ) {
                Recycle val = mRecycleMap.get( key );
                if( val == null || val.isRecycle() ) continue;
                recycle = val;
                break;
            }
            if( recycle != null ) {
                //重置复用Recycle
                animView = recycle.getView();
                recycle.setNewRecycle( false );
            }
            //创建复用Recycle/View
            if( recycle == null || animView == null ) {
                animView = onAnimationBuilder.onCreateAnimView( getContext(), holder, type );
                int id = animView.getId();
                if( id <= 0 ) id = ViewCompat.generateViewId();
                animView.setId( id );
                //创建复用Recycle
                recycle = new Recycle();
                recycle.setNewRecycle( true );

                LogUtil.d( TAG, "startAnim -> recycle -> id:%s, type:%s",
                        recycle.getId(), type
                );
                LogUtil.d( TAG, "startAnim -> recycle(new) -> id:%s, type:%s", id, type );
            }
            ViewGroup.LayoutParams lp = animView.getLayoutParams();
            if( lp == null ) {
                lp = new ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                );
            }
            //绑定布局参数
            onAnimationBuilder.onAnimViewLayoutParams( holder, lp, type );
            animView.setLayoutParams( lp );
            recycle.setView( animView );
            recycle.setRecycle( true );
            recycle.setHolderType( type );
            return recycle;
        }

        /**
         绑定Holder
         @param holder          Holder
         @param vgContent       android.R.id.content
         @param recycle         复用Recycle
         @param call            处理回调
         */
        private void doBindHolder(AnimationHolder holder, ViewGroup vgContent,
                                  Recycle recycle, Consumer<Boolean> call) {
            Context context = getContext();
            if( context == null ) return;
            OnAnimationBuilder onAnimationBuilder = holder.getOnAnimationBuilder();
            int type = holder.getHolderType();

            View fromView = holder.getFromView();
            View toView = holder.getToView();
            View animView = recycle.getView();
            ViewGroup.LayoutParams lpAnimView = animView.getLayoutParams();

            //启用起始View中心原点对齐
            int fromAlignType = onAnimationBuilder.onEnableFromAlign( type );
            if( fromAlignType != OnAnimationBuilder.AlignType.DEFAULT ) {
                View view = fromView == null ? animView : fromView;
                holder.getFromViewXY( mTmpXY );
                getViewCenterAlign(
                        "FromView", view, fromView == null, fromAlignType
                );
                holder.setFromXY( mTmpXY[ 0 ], mTmpXY[ 1 ] );
                LogUtil.d( TAG, "startAnim -> enableFromViewAlign -> type:%s, fXY:%s|%s",
                        type, holder.getFromViewX(), holder.getFromViewY()
                );
            }

            //启用目标View中心原点对齐
            int toAlignType = onAnimationBuilder.onEnableToAlign( type );
            if( toAlignType != OnAnimationBuilder.AlignType.DEFAULT ) {
                View view = toView == null ? animView : toView;
                holder.getToViewXY( mTmpXY );
                getViewCenterAlign(
                        "ToView", view, toView == null, toAlignType
                );
                holder.setToXY( mTmpXY[ 0 ], mTmpXY[ 1 ] );
                LogUtil.d( TAG, "startAnim -> enableToViewAlign -> type:%s, tXY:%s|%s",
                        type, holder.getToViewX(), holder.getToViewY()
                );
            }

            LogUtil.d( TAG, "startAnim -> " +
                            "animWH:%s|%s, fvXY:%s|%s, tvXY:%s|%s",
                    lpAnimView.width, lpAnimView.height,
                    holder.getFromViewX(), holder.getFromViewY(),
                    holder.getToViewX(), holder.getToViewY()
            );

            Animation addAnim = holder.getFromAddAnimation();

            //复用机制
            recycle.setView( animView );
            mRecycleMap.put( recycle.getId(), recycle );

            //添加到主页面
            if( recycle.isNewRecycle() ) {
                try {
                    //如果存在则先移除
                    if( vgContent.indexOfChild( animView ) != -1 ) {
                        vgContent.removeView( animView );
                    }
                    vgContent.addView( animView, lpAnimView );
                }catch(Exception e) {
                    e.printStackTrace();
                    LogUtil.e( TAG, "startAnim -> addView error!, msg:" + e.getMessage() );
                }
                LogUtil.d( TAG, "startAnim -> new recycle addView -> id:%s, type:%s, recycleCount:%s",
                        recycle.getId(), type, mRecycleMap.size()
                );
            }

            setVisibility( animView, View.VISIBLE );
            animView.setAlpha( 0F );

            //播放进入动画
            if( addAnim != null ) {
                addAnim.setAnimationListener( new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        //如果不是alpha动画则显示alpha
                        if( !( addAnim instanceof AlphaAnimation ) ) {
                            animView.setAlpha( holder.getAnimViewAlpha() );
                        }
                    }
                    @Override
                    public void onAnimationEnd(Animation animation) { }
                    @Override
                    public void onAnimationRepeat(Animation animation) { }
                } );
                animView.startAnimation( addAnim );
            }
            AnimationXY animXY = holder.getAnimatorXY();
            int aw = lpAnimView.width;
            int ah = lpAnimView.height;
            int fromX = checkXY( context, holder.getFromViewX(), aw, ah, true );
            int toX = checkXY( context, holder.getToViewX(), aw, ah, true );
            int fromY = checkXY( context, holder.getFromViewY(), aw, ah, false );
            int toY = checkXY( context, holder.getToViewY(), aw, ah, false );
            long durationX = holder.getXDuration();
            long durationY = holder.getYDuration();
            long fromDelayMillis = holder.getFromDelayMillis();

            LogUtil.d( TAG, "startAnim -> from:%s|%s, to:%s|%s, animViewVis:%s",
                    fromX, fromY, toX, toY, animView.getVisibility() == View.VISIBLE
            );

            int[] pathsX = holder.getPathXs();
            int[] pathsY = holder.getPathYs();
            if( animXY == null ) animXY = AnimationXY.create();
            if( durationX > 0 ) {
                animXY.updateX( fromX, toX, pathsX );
                animXY.setDurationX( durationX );
                animXY.setInterpolatorX( holder.getXInterpolator() );
            }
            if( durationY > 0 ) {
                animXY.updateY( fromY, toY, pathsY );
                animXY.setDurationY( durationY );
                animXY.setInterpolatorY( holder.getYInterpolator() );
            }
            holder.setAnimatorXY( animXY );

            AnimatorListenerAdapter animAdapter = new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart( animation );
                    boolean isStart = holder.isAnimStart();
                    float alpha = holder.getAnimViewAlpha();
                    if( isStart ) {
                        setVisibility( animView, View.VISIBLE );
                        animView.setAlpha( alpha );
                        post( () -> {
                            if( call != null ) call.accept( false );
                        } );
                        long durXY = Math.max( durationX, durationY );
                        /* 缩放动画，从正常到消失 */
                        long toScaleDur = holder.getToScaleMillis();
                        if( toScaleDur > 0 ) {
                            post( () ->
                                            setAnimViewScale( animView, false, toScaleDur ),
                                    Math.abs( durXY - toScaleDur )
                            );
                        }
                        /* 移除动画 */
                        Animation removeAnim = holder.getToRemoveAnimation();
                        if( removeAnim != null ) {
                            post( () -> {
                                removeAnim.setAnimationListener( new Animation.AnimationListener() {
                                    @Override
                                    public void onAnimationStart(Animation animation) { }
                                    @Override
                                    public void onAnimationEnd(Animation animation) {
                                        //回收资源
                                        recycle();
                                    }
                                    @Override
                                    public void onAnimationRepeat(Animation animation) { }
                                } );
                                animView.startAnimation( removeAnim );
                            }, Math.abs( durXY - removeAnim.getDuration() ) );
                        }
                    }
                    LogUtil.d( TAG,
                            "animAdapter -> onAnimationStart -> isStart:%s, animViewVis:%s",
                            isStart, animView.getVisibility() == View.VISIBLE
                    );
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd( animation );
                    boolean isEnd = holder.isAnimEnd();
                    if( isEnd ) {
                        //没有移除动画时，回收资源
                        if( holder.getToRemoveAnimation() == null ) recycle();
                    }
                    LogUtil.d( TAG, "animAdapter -> onAnimationEnd -> isEnd:%s", isEnd );
                }

                private void recycle() {
                    setVisibility( animView, View.GONE );
                    animView.setX( 0 );
                    animView.setY( 0 );
                    recycle.setView( animView );
                    recycle.setRecycle( false );
                    mRecycleMap.put( recycle.getId(), recycle );
                    if( call != null ) call.accept( true );
                }
            };
            post( () -> {
                AnimationXY holderAnimXY = holder.getAnimatorXY();
                if( holderAnimXY == null ) {
                    LogUtil.e( TAG, "startAnim -> holderAnimXY is null. fromDelayMillis:%s",
                            fromDelayMillis
                    );
                    return;
                }
                animView.setX( fromX );
                animView.setY( fromY );
                holderAnimXY.startValues( animView, animAdapter, holder.isEnableTranslate() );
                /* 缩放动画，从消失到正常 */
                long fromScaleDur = holder.getFromScaleMillis();
                if( fromScaleDur > 0 ) {
                    setAnimViewScale( animView, true, fromScaleDur );
                }
            }, fromDelayMillis);
        }

        /**
         设置缩放动画
         */
        private void setAnimViewScale(View view, boolean smallToNormal, long dur) {
            float from = smallToNormal ? 0F : 1F;
            float to = smallToNormal ? 1F : 0F;
            ValueAnimator vaAnimScaleX = ValueAnimator.ofFloat( from, to );
            ValueAnimator vaAnimScaleY = ValueAnimator.ofFloat( from, to );

            vaAnimScaleX.setDuration( dur );
            vaAnimScaleY.setDuration( dur );

            vaAnimScaleX.addUpdateListener( animation ->
                    view.setScaleX( ObjUtils.parseFloat( animation.getAnimatedValue() ) )
            );
            vaAnimScaleY.addUpdateListener( animation ->
                    view.setScaleY( ObjUtils.parseFloat( animation.getAnimatedValue() ) )
            );

            vaAnimScaleX.start();
            vaAnimScaleY.start();
        }

        /**
         获取View中心原点对齐XY坐标
         */
        private void getViewCenterAlign(@NonNull String tag, @Nullable View view, boolean isAnimView,
                                        @OnAnimationBuilder.AlignType int alignType) {
            if( view == null ) return;
            Context context = getContext();
            if( context == null ) return;
            getViewSize( view, mTmpViewSize );
            int width = mTmpViewSize[ 0 ];
            int height = mTmpViewSize[ 1 ];
            int x = mTmpXY[ 0 ];
            int y = mTmpXY[ 1 ];
            int centerAlignX = 0;
            int centerAlignY = 0;
            //不禁用，阿拉伯语环境下会出问题
            if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 ) {
                view.setLayoutDirection( View.LAYOUT_DIRECTION_LTR );
            }
            /* 如果是AnimView，中心以AnimView为主，From/ToView以目标View为主 */
            x = checkXY( context, x, width, height, true );
            y = checkXY( context, y, width, height, false );
            boolean allAlign = alignType == OnAnimationBuilder.AlignType.IN_PARENT;
            if( allAlign || alignType == OnAnimationBuilder.AlignType.HORIZONTAL ) {
                centerAlignX = mPool.getCenterAlign( width, isAnimView );
                x = isAnimView ? x - centerAlignX : x + centerAlignX;
            }
            if( allAlign || alignType == OnAnimationBuilder.AlignType.VERTICAL ) {
                centerAlignY = mPool.getCenterAlign( height, isAnimView );
                y = isAnimView ? y - centerAlignY : y + centerAlignY;
            }
            AnimationPool.mTmpXY[ 0 ] = x;
            AnimationPool.mTmpXY[ 1 ] = y;
            LogUtil.d( TAG, "AnimationPool -> tag:%s -> isAnimView:%s, alignType:%s, " +
                            "wh:%s|%s, centerAlignXY:%s|%s, alignXY:%s|%s",
                    tag, isAnimView, alignType, width, height,
                    centerAlignX, centerAlignY, AnimationPool.mTmpXY[ 0 ], AnimationPool.mTmpXY[ 1 ]
            );
        }

        private int checkXY(Context context, int xy, int width, int height, boolean isX) {
            if( xy == AnimationHolder.START_OUTSIDE ) {
                xy = -( isX ? width : height );
            }else if( xy == AnimationHolder.END_OUTSIDE ) {
                xy = isX ?
                        SysUtil.getScreenWidth( context ) + width :
                        SysUtil.getScreenHeight( context ) + height;
            }
            LogUtil.d(
                    TAG, "AnimationPool -> checkXY -> xy:%s, width:%s, height:%s, isX:%s",
                    xy, width, height, isX
            );
            return xy;
        }

        private void setVisibility(View view, int vis) {
            if( view != null ) view.setVisibility( vis );
        }
    }
}
